package jee.boot.module.file.oss;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import jee.boot.common.basic.ServiceException;
import jee.boot.common.standard.DocumentStorageClient;
import jee.boot.common.utils.SpringContextHolder;
import jee.boot.module.file.attachment.dao.AttachmentDao;
import jee.boot.entity.file.Attachment;
import jee.boot.module.file.attachment.service.AttachmentService;
import jee.boot.module.file.utils.FileStoreConst;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.UUID;

/**
 * 文件的本地存储方式
 */
public class OssFileStore implements DocumentStorageClient {
    private Logger logger= LoggerFactory.getLogger(getClass());
    private OssFileConfig config;
    private AttachmentService attachmentService;
    private AttachmentDao attachmentDao;
    public OssFileStore(OssFileConfig baseConfig) {
        this.config = baseConfig;
    }

    /**
     * 附件数据存储服务-service
     * @return
     */
    public AttachmentService getAttachmentService() {
        if(attachmentService == null){
            attachmentService = SpringContextHolder.getBean(AttachmentService.class);
        }
        return attachmentService;
    }

    /**
     * 附件数据存储服务-dao
     * @return
     */
    public AttachmentDao getAttachmentDao() {
        if(attachmentDao == null){
            attachmentDao = SpringContextHolder.getBean(AttachmentDao.class);
        }
        return attachmentDao;
    }

    @Override
    public Attachment uploadFromMultipartFile(MultipartFile multipartFile) {
        return uploadFromMultipartFile(multipartFile, FileStoreConst.SYMBOL_BLANK, FileStoreConst.SYMBOL_BLANK);
    }

    /**
     * OSS附件上传 - MultipartFile
     * @param multipartFile
     * @return
     */
    @Override
    public Attachment uploadFromMultipartFile(MultipartFile multipartFile, String businessId, String businessType) {
        logger.info("OssFileStore - uploadFromMultipartFile:::");

        try {
            return this.uploadFromInputStream(multipartFile.getInputStream(), multipartFile.getOriginalFilename(), businessId, businessType, multipartFile.getSize());
        }  catch(ServiceException e) {
            e.printStackTrace();
            throw e;
        } catch(Exception e) {
            e.printStackTrace();
            throw new ServiceException(FileStoreConst.MSG_BT_COMMON_999);
        }
    }

    /**
     * OSS附件上传 - File
     * @param file
     * @return
     */
    @Override
    public Attachment uploadFromFile(File file) {
        logger.info("OssFileStore - uploadFromFile:::");

        try {
            return this.uploadFromInputStream(new FileInputStream(file), file.getName(), FileStoreConst.SYMBOL_BLANK, FileStoreConst.SYMBOL_BLANK, file.length());
        }  catch(ServiceException e) {
            e.printStackTrace();
            throw e;
        } catch(Exception e) {
            e.printStackTrace();
            throw new ServiceException(FileStoreConst.MSG_BT_COMMON_999);
        }
    }

    @Override
    public Attachment uploadFromInputStream(InputStream inputStream, String fileName) {
        return uploadFromInputStream(inputStream, fileName, FileStoreConst.SYMBOL_BLANK, FileStoreConst.SYMBOL_BLANK, 0);
    }

    /**
     * OSS附件上传 - InputStream
     * @param inputStream
     * @param fileName
     * @return
     */
    @Override
    public Attachment uploadFromInputStream(InputStream inputStream, String fileName, String businessId, String businessType, long fileSize) {
        logger.info("OssFileStore - uploadFromInputStream:::");

        try {
            // 创建连接
            OSS ossClient = getOssClient();

            // 创建文件唯一路径标识
            String fileId =  UUID.randomUUID().toString().replace(FileStoreConst.SYMBOL_DASH, FileStoreConst.SYMBOL_BLANK);
            String filePath = config.getFolder() + FileStoreConst.SYMBOL_SLASH + fileId + FileStoreConst.SYMBOL_SLASH;
            String filePathName = filePath + fileName;

            // 设置头部信息
            ObjectMetadata objectMetadata = new ObjectMetadata();
            if(FileStoreConst.DEFAULT_FILE_TYPE_JPG.equals(FileStoreConst.getSuffixFileName(fileName).toLowerCase())) {
                objectMetadata.setContentType(FileStoreConst.DEFAULT_FILE_TYPE_PRE + FileStoreConst.DEFAULT_FILE_TYPE_JPG);
            } else if(FileStoreConst.DEFAULT_FILE_TYPE_PNG.equals(FileStoreConst.getSuffixFileName(fileName).toLowerCase())) {
                objectMetadata.setContentType(FileStoreConst.DEFAULT_FILE_TYPE_PRE + FileStoreConst.DEFAULT_FILE_TYPE_JPG);
            } else if(FileStoreConst.DEFAULT_FILE_TYPE_BMP.equals(FileStoreConst.getSuffixFileName(fileName).toLowerCase())) {
                objectMetadata.setContentType(FileStoreConst.DEFAULT_FILE_TYPE_PRE + FileStoreConst.DEFAULT_FILE_TYPE_BMP);
            } else if(FileStoreConst.DEFAULT_FILE_TYPE_GIF.equals(FileStoreConst.getSuffixFileName(fileName).toLowerCase())) {
                objectMetadata.setContentType(FileStoreConst.DEFAULT_FILE_TYPE_PRE + FileStoreConst.DEFAULT_FILE_TYPE_GIF);
            }

            // 上传文件
            try {
                ossClient.putObject(config.getBucketName(), filePathName, inputStream, objectMetadata);
            } catch(Exception e) {
                e.printStackTrace();
                throw new ServiceException(FileStoreConst.MSG_BT_COMMON_008);
            }

            // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
            Date expiration = new Date(new Date().getTime() + FileStoreConst.DEFAULT_FILE_EXPIRE);
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(config.getBucketName(), filePathName, HttpMethod.GET);
            req.setExpiration(expiration);
            URL fileUrl = ossClient.generatePresignedUrl(req);

            // 关闭连接
            ossClient.shutdown();

            // 上传完成，返回实例数据
            Attachment attachment = new Attachment();
            attachment.preInsert();
            attachment.setFileId(fileId);
            attachment.setFileName(fileName);
            attachment.setFileType(FileStoreConst.getSuffixFileName(fileName));
            attachment.setFileSize(new BigDecimal(fileSize / FileStoreConst.DEFAULT_FILE_LEVEL));
            attachment.setFileUrl(fileUrl.toString());
            attachment.setSavePath(filePath);

            // 保存附件信息
            getAttachmentDao().insert(attachment);

            // 返回附件信息
            return attachment;
        }  catch(ServiceException e) {
            e.printStackTrace();
            throw e;
        } catch(Exception e) {
            e.printStackTrace();
            throw new ServiceException(FileStoreConst.MSG_BT_COMMON_999);
        }
    }

    /**
     * 附件下载
     * @param attachmentId
     * @return
     * @throws FileNotFoundException
     */
    public File downloadToFile(String attachmentId) throws FileNotFoundException {
        logger.info("downloadToFile");
        Attachment attachment = getAttachmentService().get(new Attachment(attachmentId));
        if (attachment == null) {
            logger.error("附件不存在:" + attachmentId);
            throw new FileNotFoundException(attachmentId);
        }

        // 创建连接
        OSS ossClient = getOssClient();
        File retFile = new File(config.getLocalPath() + attachment.getFileName());
        if(retFile.exists()) {
            String fileNamePre = attachment.getFileName().replace( FileStoreConst.SYMBOL_PRICK + attachment.getFileType(), FileStoreConst.SYMBOL_BLANK);
            retFile = new File(config.getLocalPath() + fileNamePre + FileStoreConst.SYMBOL_DASH + UUID.randomUUID().toString().replace(FileStoreConst.SYMBOL_DASH, FileStoreConst.SYMBOL_BLANK) + FileStoreConst.SYMBOL_PRICK + attachment.getFileType());
        }
        ossClient.getObject(new GetObjectRequest(config.getBucketName(), attachment.getSavePath() + attachment.getFileName()), retFile);

        // 关闭OSSClient。
        ossClient.shutdown();

        return retFile;
    }

    @Override
    public OutputStream downloadToOutputStream(String attachmentId) throws FileNotFoundException {
        return null;
    }

    /**
     * 附件下载
     * @param attachmentId
     * @param attachmentId
     * @throws FileNotFoundException
     */
    public void downloadToOutputStream(String attachmentId, HttpServletResponse httpServletResponse) throws ServiceException {
        logger.info("OssFileStore - downloadToOutputStream:::");

        try {
            Attachment Attachment = getAttachment(attachmentId);
            if(Attachment == null) {
                // 附件不存在
                throw new ServiceException(FileStoreConst.MSG_BT_COMMON_007);
            }

            // 创建连接
            OSS ossClient = getOssClient();

            // 文件下载
            OSSObject ossObject;
            try {
                ossObject = ossClient.getObject(config.getBucketName(), Attachment.getSavePath() + Attachment.getFileName());
            } catch(Exception e) {
                e.printStackTrace();
                throw new ServiceException(FileStoreConst.MSG_BT_COMMON_009);
            }

            // 设置响应头
            setDownloadHeader(Attachment.getFileName(), httpServletResponse);

            // 准备返回输出流
            IOUtils.copy(ossObject.getObjectContent(), httpServletResponse.getOutputStream());

            ossClient.shutdown();
        } catch(ServiceException e) {
            e.printStackTrace();
            throw e;
        } catch(Exception e) {
            e.printStackTrace();
            throw new ServiceException(FileStoreConst.MSG_BT_COMMON_999);
        }
    }

    /**
     * 附件删除
     * @param attachmentId
     * @throws FileNotFoundException
     */
    public void deleteFile(String attachmentId) throws ServiceException {
        logger.info("OssFileStore - deleteFile:::");

        try {
            // 验证数据库是否存在附件
            Attachment Attachment = getAttachment(attachmentId);
            if(Attachment == null) {
                // 附件不存在
                throw new ServiceException(FileStoreConst.MSG_BT_COMMON_007);
            }

            // 创建连接
            OSS ossClient = getOssClient();

            // OSS数据删除
            try {
                ossClient.deleteObject(config.getBucketName(), Attachment.getSavePath() + Attachment.getFileName());
            } catch(Exception e) {
                e.printStackTrace();
                throw new ServiceException(FileStoreConst.MSG_BT_COMMON_010);
            }

            // 数据库数据删除
            getAttachmentDao().delete(Attachment);
        } catch(ServiceException e) {
            e.printStackTrace();
            throw e;
        } catch(Exception e) {
            e.printStackTrace();
            throw new ServiceException(FileStoreConst.MSG_BT_COMMON_999);
        }
    }



    /** *********  私有方法 ********* **/
    /**
     * 获取OSS连接
     * @return
     */
    private OSS getOssClient() {
        return new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
    }

    /**
     * 查询附件实例数据
     * @param attachmentId
     * @return
     */
    private Attachment getAttachment(String attachmentId) {
        Attachment Attachment = new Attachment(attachmentId);
        return this.getAttachmentService().get(Attachment);
    }

    /**
     * 设置Header信息
     * @param fileName
     * @param httpServletResponse
     */
    private void setDownloadHeader(String fileName, HttpServletResponse httpServletResponse) throws Exception {
        httpServletResponse.setContentType("application/octet-stream" + ";charset=UTF-8");
        // 文件编码 处理文件名中的 '+'、' ' 特殊字符
        String encoderName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20").replace("%2B", "+");
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=\"" + encoderName + "\"");
        // response.setContentLength(fileByte.length);
        httpServletResponse.setHeader("Pragma", "No-cache");
        httpServletResponse.setHeader("Cache-Control", "No-cache");
        httpServletResponse.setDateHeader("Expires", 0L);
    }
}
